#pragma once

#pragma pack(1)

typedef struct FLEXFILEHEADER
{
	char	title[0x50];	// (optionnal, filled with 00s)
	long	magic1;			// (seems to be always $FFFF1A00)
	long	count;			// (number of object in table, including empty objects)
	long	magic2;			// (seems to be always $000000CC)
	long	d[9];			// (often set to 0, but sometimes used, meaning?)
} FLEXFILEHEADER, *PFLEXFILEHEADER;

typedef struct FLEXFILEENTRY
{
	long	offset;			// (relative to the beginning of the file)
	long	size;			// note: empty objects are referenced as null offset and null size
} FLEXFILEENTRY, *PFLEXFILEENTRY;

typedef struct FLEXARCHIVEFILE
{
	char filename[13];
	char content[1];
} FLEXARCHIVEFILE, *PFLEXARCHIVEFILE;

/*
Palette
A palette is an array describing the RGB values for 256 color indexes
This format is compatible with the bios call for setting a palette.
A palette can be found in a flex file or as a separate .PAL file
Format:
  palette = 256 rgb
  rgb     = red green blue
  red     = byte (0..3F)
  green   = byte (0..3F)
  blue    = byte (0..3F)
*/
typedef struct COLORENTRY
{
	union {
		struct {
			char red;			// (0..3F)
			char green;			// (0..3F)
			char blue;			// (0..3F)
		};
		char components[3];
	};
} COLORENTRY, *PCOLORENTRY;

#define MAX_COLOR_ENTRIES 256

typedef COLORENTRY PALETTE[MAX_COLOR_ENTRIES];

typedef unsigned char (*XFORMTBL)[256];

/*
 *	Palette #'s in 'palettes.flx':
 */
const int PALETTE_DAY			= 0;
const int PALETTE_DUSK			= 1;
const int PALETTE_DAWN			= 1;	// Think this is it.
const int PALETTE_NIGHT			= 2;
const int PALETTE_INVISIBLE		= 3;	// When Avatar is invisible.
const int PALETTE_OVERCAST		= 4;	// When raining or overcast during daytime
const int PALETTE_FOG			= 5;
					// 6 looks a little brighter than #2.
					// 7 is somewhat warmer.  Torch?
const int PALETTE_RED			= 8;	// Used when hit in combat.
					// 9 has lots of black.
const int PALETTE_LIGHTNING		= 10;
const int PALETTE_SINGLE_LIGHT	= 11;
const int PALETTE_MANY_LIGHTS	= 12;


/*
Tiles
This is a set of graphic tile representing a certain type of floor that
can appears on the ground in the game view. One tile is 8x8 pixel large
Format:
  tiles = set of tile
  tile  = 8x8 pixel
  pixel = byte (index inside the color palette)
*/
const int PIXELS_PER_TILE_IN_WIDTH = 8; // pixels
const int PIXELS_PER_TILE_IN_HEIGHT = 8;// pixels
const int PIXELS_PER_TILE = PIXELS_PER_TILE_IN_WIDTH * PIXELS_PER_TILE_IN_HEIGHT;
typedef char (*TILE)[PIXELS_PER_TILE_IN_WIDTH];

/*
Shape
This is the graphic representing any item that appears on the game view
except floor tiles. The graphic can include transparent area.
A shape represent a type of object. One shape can have many frames representing
the different appearance of this kind of object. Each frame is describe as a
set of "slices" composing the non-transparent area of the graphic.
A shape can be found in a flex file or as a separate .SHP file
Format:
  shape   = size, set of offset, set of frame
  size    = longint (should be same as in flex table)
  offset  = longint (one for each frame, relative to beginning of shape)
  frame   = rightX, leftX, leftY, rightY, set of slice
    (coordinates are positive distance from the hot spot of the shape)
  slice   = word=0000   (if end of frame)
          | slength, offsetX, offsetY, scontent
    (a slice represents several pixels on the same line)
  slength = word [b0    =type of slice (0=standard, 1=compressed)
                  b1..bF=length in pixel]
  offsetX = integer (relative to hot spot)
  offsetY = integer (relative to hot spot)
  scontent= set of pixel  (if standard slice)
          | set of block  (if compressed slice)
  block   = blength, bcontent
  blength = byte [b0    =type of block (0=standard, 1=repeated pixel)
                  b1..b7=length in pixel]
  bcontent= set of pixel  (if standard block)
          | pixel         (if repeated pixel block)
*/
typedef struct SHAPEENTRY
{
	long	size;			// (should be same as in flex table)
	long	offset[1];		// (one for each frame, relative to beginning of shape)
} SHAPEENTRY, *PSHAPEENTRY;

typedef struct FRAMEENTRY
{
	short	rightX;			// (coordinates are positive distance from the hot spot of the shape)
	short	leftX;
	short	topY;
	short	bottomY;
	unsigned char slice[1];
} FRAMEENTRY, *PFRAMEENTRY;

typedef struct SLICEENTRY
{
	unsigned short type:1;		// (0=standard, 1=compressed)
	unsigned short length:15;	// length in pixel
	short	offsetX;			// (relative to hot spot)
	short	offsetY;			// (relative to hot spot)
	unsigned char content[1];			// set of pixel  (if standard slice) | set of block  (if compressed slice)
} SLICEENTRY, *PSLICEENTRY;

typedef struct SLICECONTENT
{
	unsigned char type:1;		// type of block (0=standard, 1=repeated pixel)
	unsigned char length:7;		// length in pixel
	unsigned char pixel[1];			// set of pixel  (if standard block) | pixel (if repeated pixel block)
} SLICECONTENT, *PSLICECONTENT;

/*
U7CHUNKS:     $C00 chunks
  chunk   = 16x16 shapeID
  shapeID = word [b0..b9=shapeType  (0..$400-1)
                  bA..bE=shapeFrame (0..$20-1)
                  bF=? ]
*/
typedef struct SHAPEID
{
	unsigned short shapeType:10;
	unsigned short shapeFrame:5;
	unsigned short xyflip:1; // indicate horz and vert are exchanged.
} SHAPEID, *PSHAPEID;


const int TILES_PER_CHUNK_IN_WIDTH = 16; // tiles
const int TILES_PER_CHUNK_IN_HEIGHT = 16;// tiles
const int TILES_PER_CHUNK = TILES_PER_CHUNK_IN_WIDTH * TILES_PER_CHUNK_IN_HEIGHT;

const int PIXELS_PER_CHUNK_IN_WIDTH = PIXELS_PER_TILE_IN_WIDTH * TILES_PER_CHUNK_IN_WIDTH;
const int PIXELS_PER_CHUNK_IN_HEIGHT = PIXELS_PER_TILE_IN_HEIGHT * TILES_PER_CHUNK_IN_HEIGHT;

const int CHUNKS_PER_SUPERCHUNK_IN_WIDTH = 16; // chunks
const int CHUNKS_PER_SUPERCHUNK_IN_HEIGHT = 16;// chunks
const int CHUNKS_PER_SUPERCHUNK = CHUNKS_PER_SUPERCHUNK_IN_WIDTH * CHUNKS_PER_SUPERCHUNK_IN_HEIGHT;

const int SUPERCHUNKS_PER_MAP_IN_WIDTH = 12;
const int SUPERCHUNKS_PER_MAP_IN_HEIGHT = 12;
const int SUPERCHUNKS_PER_MAP = SUPERCHUNKS_PER_MAP_IN_WIDTH * SUPERCHUNKS_PER_MAP_IN_HEIGHT;

const int PIXELS_PER_SUPERCHUNK_IN_WIDTH = CHUNKS_PER_SUPERCHUNK_IN_WIDTH * PIXELS_PER_CHUNK_IN_WIDTH;
const int PIXELS_PER_SUPERCHUNK_IN_HEIGHT = CHUNKS_PER_SUPERCHUNK_IN_HEIGHT * PIXELS_PER_CHUNK_IN_HEIGHT;

// U7CHUNKS
typedef SHAPEID (*CHUNK_PATTERN)[TILES_PER_CHUNK_IN_HEIGHT][TILES_PER_CHUNK_IN_WIDTH];

// U7MAP
typedef unsigned short PATTERN_ID, *PPATTERN_ID; // 0..0xc00-1
typedef PATTERN_ID (*MAP)[SUPERCHUNKS_PER_MAP_IN_WIDTH][CHUNKS_PER_SUPERCHUNK_IN_HEIGHT][CHUNKS_PER_SUPERCHUNK_IN_WIDTH];

const int CHUNKS_PER_MAP_IN_WIDTH  = SUPERCHUNKS_PER_MAP_IN_WIDTH * CHUNKS_PER_SUPERCHUNK_IN_WIDTH; // 12 * 16 = 192
const int TILES_PER_MAP_IN_WIDTH   = CHUNKS_PER_MAP_IN_WIDTH * TILES_PER_CHUNK_IN_WIDTH; // 12 * 16 * 16
const int PIXELS_PER_MAP_IN_WIDTH  = TILES_PER_MAP_IN_WIDTH * PIXELS_PER_TILE_IN_WIDTH; // 12 * 16 * 16 * 8 = 24576

const int CHUNKS_PER_MAP_IN_HEIGHT = SUPERCHUNKS_PER_MAP_IN_HEIGHT * CHUNKS_PER_SUPERCHUNK_IN_HEIGHT; // 12 * 16
const int TILES_PER_MAP_IN_HEIGHT  = CHUNKS_PER_MAP_IN_HEIGHT * TILES_PER_CHUNK_IN_HEIGHT; // 12 * 16 * 16
const int PIXELS_PER_MAP_IN_HEIGHT = TILES_PER_MAP_IN_HEIGHT * PIXELS_PER_TILE_IN_HEIGHT; // 12 * 16 * 16 * 8 = 24576

/*
SHPDIMS.DAT:  $400-$96 shpdims  (one for each Game Shape)
  shpdims = dimY, dimX
  dimY    = byte [b0: obstacle in N-S direction?
                  b1..b7: Y dimension of the shape=number of tiles covered?]
  dimX    = byte [b0: obstacle in W-E direction?
                  b1..b7: X dimension of the shape=number of tiles covered?]
*/
typedef struct SHPDIMS
{
	unsigned char yobstacle:1;
	unsigned char xdims:7; // tiles
	unsigned char xobstacle:1;
	unsigned char ydims:7; // tiles
} SHPDIMS, *PSHIPDIMS;

/*
WGTVOL.DAT:   $400 wgtvol       (one for each Game Shape & Tiles)
  wgtvol  = weight, volume
  weight  = byte: weight of the shape (in 0.1 stone)
  volume  = byte: volume of the shape
    (stone is the unit used in the inventory window in the game)
*/
typedef struct WGTVOL
{
	unsigned char weight;
	unsigned char volume;
} WGTVOL, *PWGTVOL;

/*
TFA.DAT:      $400 entries (one for each Game Shape & Tiles)
  tfa     = $400 triplet
  triplet = tfa1, tfa2, tfa3
  tfa1    = byte [b0..1 =?
                  b2    =animated shape
                  b3..4 =?
                  b5..b7=height of shape]
  tfa2    = byte [b0..b6=?

                  b7    =is transparent (cannot be selected, cannot place item on it)]
  tfa3    = byte [b0..b2=X size of the shape - 1  (number of tiles covered)
                  b3..b5=Y size of the shape - 1  (number of tiles covered)
                  b6    =is a light source
                  b7    =contains transparency colors (light,dark,blood)]
*/

enum SHAPE_CLASS
{
	SC_UNUSABLE		= 0,	// Trees.
	SC_QUALITY		= 2,
	SC_QUANTITY		= 3,	// Can have more than 1:  coins, arrs.
	SC_HAS_HP		= 4,    // Breakable items (if hp != 0, that is)
	SC_QUALITY_FLAGS= 5,	// Item quality is set of flags:
	                        // Bit 0 = invisible
	                        // Bit 3 = okay-to-take.
	SC_CONTAINER	= 6,
	SC_HATCHABLE	= 7,	// Eggs, traps, moongates.
	SC_SPELLBOOK	= 8,
	SC_BARGE		= 9,
	SC_VIRTUALSTONE	= 11,
	SC_MONSTER		= 12,	// Non-human's.
	SC_HUMAN		= 13,	// Human NPC's.
	SC_BUILDING		= 14	// Roof, window, mountain.
};

typedef struct TFA
{
	// tfa1
	unsigned char sound_effect:1;
	unsigned char strange_movement:1; // Slimes, sea monsters.
	unsigned char animated:1;
	unsigned char solid:1;
	unsigned char water:1;
	unsigned char ztiles:3; // starts from 0
	// tfa2
	unsigned char shapeClass:4; // see 'enum SHAPE_CLASS'
	unsigned char field:1;
	unsigned char door:1;
	unsigned char barge_part:1;
	unsigned char translucent:1;
	// tfa3
	unsigned char xtiles:3; // x size of the shape - 1
	unsigned char ytiles:3; // y size of the shape - 1
	unsigned char light_source:1;
	unsigned char containsTransparencyColors:1;
} TFA, *PTFA;

/*
U7IFIXnn    = flex file with fixed items list
  (nn represents the region number.
   There are 16x16 lists in each file, one for each chunk in the region.
   The lists contain the static items found in the given chunk.
   Static items are items which can't interact with the user)
  list    = entries (each entry is 4 bytes long)
  entry   = coord, lift, shapeID
  coord   = byte [b0..b3=Y coord inside chunk
                  b4..b7=X coord inside chunk]
  lift    = byte [b0..b3=lift level
                  b4..b7=0]
  shapeID = int16 [b0..b9=shapeType  (0..$400-1)
                  bA..bE=shapeFrame (0..$20-1)
                  bF=0]
*/
typedef struct IFIXENTRY
{
	unsigned char yt:4;
	unsigned char xt:4;
	unsigned char zlevel:4;
	unsigned char reserved:4;
	SHAPEID shapeID;
} IFIXENTRY, *PIFIXENTRY;

/*
reg_items = 16x16 chk_items (one for each chunk of the region)
  chk_items = item, item, ..., item, 00 (or just 00 if no item for this chunk)
  item      = standard | extended | extra
  standard  = 06, XY, shapeID, lift, quality
  extended  = 0C, XY, shapeID, type, proba, data1, lift, data2, [content]
  extra     = 12, XY, shapeID, extradata
    (content is present if shapeID represents a container and type not null)
  content   = item, item, ..., item, 01
*/
typedef struct IREG_BASE_ENTRY
{
	unsigned char length;
	// 0:2
	union {
		struct {
			unsigned char xtile:4;
			unsigned char xchunk:4;
			unsigned char ytile:4;
			unsigned char ychunk:4;
		};
		unsigned char  coords[2]; // tile units in the superchunk
		unsigned short referent;
	};
	// 2:2
	SHAPEID shapeID;
} IREG_BASE_ENTRY, *PIREG_BASE_ENTRY;

typedef struct IREG_STANDARD_ENTRY
	: public IREG_BASE_ENTRY
{
	// 4:1
	unsigned char regionID:4; // ITEMNODE.DAT, inside:6, outside:0..3 - same for all items in region, 4 seems to be unused NPC
	unsigned char zlevel:4;
	// 5:1
	unsigned char quality; // if obj has SC_QUANTITY, quality&0x80: ok to take, quality & 0x7f: quantity
} IREG_STANDARD_ENTRY, *PIREG_STANDARD_ENTRY;

typedef struct IREG_EXTENDED_ENTRY
	: public IREG_BASE_ENTRY
{
	union {
		struct {
			unsigned short	type;
			unsigned char	proba;
			unsigned short	data1;
			unsigned char	lift0:4;
			unsigned char	zlevel:4;
			unsigned short	data2;
		} general;

		struct {
			unsigned short	referent; // for container objects, if this value is not zero, it means the referent of the first item.
			                          // this should be related to IBUF.extended.next ( see struct IBUF )
			unsigned char	info1;    // (? always 00 or current region number)
			unsigned char	quality;
			unsigned char	quantity;
			unsigned char	info2:4;
			unsigned char	zlevel:4;
			unsigned char	resist;
			unsigned char	flags; // b0:invisible, b3:okay to take
		} container;
	};
	//(content is present if shapeID represents a container and type not null)
} IREG_EXTENDED_ENTRY, *PIREG_EXTENDED_ENTRY;

typedef struct IREG_SPELLBOOK_ENTRY
	: public IREG_BASE_ENTRY
{
	// 4:5
	unsigned char circle1;
	unsigned char circle2;
	unsigned char circle3;
	unsigned char circle4;
	unsigned char circle5;
	// 9:1
	unsigned char  regionID:4;
	unsigned char  zlevel:4;
	// 10:4
	unsigned char circle6;
	unsigned char circle7;
	unsigned char circle8;
	unsigned char circle9;
	// 14:3
	unsigned char unknowns[3];
	// 17:1
	unsigned char bookmark;
} IREG_SPELLBOOK_ENTRY, *PIREG_SPELLBOOK_ENTRY;

//
// ITEMNODE.DAT
//
typedef struct ITEMNODE
{
	// 0
	unsigned short firstFree;
	// 2
	unsigned short countFree;
	// 4
	unsigned short dummy;
	// 6
	unsigned short regions[4][16][16]; // reference to U7IBUF.DAT
	// 2054
	unsigned char  regionIDs[4];
	// 2058
	unsigned char  curRegion;
} ITEMNODE,*PITEMNODE;

// U7IBUF.DAT
typedef struct IBUF
{
	// 8-bytes block
	union
	{
		unsigned char data[8];

		struct
		{
			unsigned short next; // next IBUF, 0x0000 when no more item
			union {
				struct {
					unsigned char xtile:4;
					unsigned char xchunk:4;
					unsigned char ytile:4;
					unsigned char ychunk:4;
				};
				unsigned char  coords[2]; // tile units in the superchunk
				unsigned short referent;
			};
			SHAPEID			shapeID;
			union {
				struct {
					unsigned char info:4;
					unsigned char zlevel:4;
					unsigned char flag;
				};
				unsigned short ext_ref; // referent to additionnal infos for extended items
			}; 
		} standard;

		struct
		{
			unsigned short	next; // next IBUF, 0x0000 when no more item
			unsigned char	superchunk; // 0 - 143
			unsigned short	data1;
			unsigned char	info:4;
			unsigned char	zlevel:4;
			unsigned char	data2; // hp for actors.
			unsigned char	flags; // b0:invisible, b3:okay to take
		} extended;
	};
} IBUF, *PIBUF;

typedef struct NBUF
{
	unsigned short	id;			// 0
	unsigned short	referent;	// 2, in U7IBUF.DAT
	unsigned short	status;		// 4
	unsigned char	strength;	// 5
	unsigned char	dexterity ;	// 6
	unsigned char	intelligence;// 7
	unsigned char	combat;		// 8
	unsigned char	activity;	// 9 activity  = byte (should be 0..31)
	unsigned char	DAM;		// 10 (default attack mode 0..9)
	unsigned char	d_1[3];		// 11
	unsigned short	status2;	// 14
	unsigned char	index2;		// 16
	unsigned char	d_2[2];		// 17
	unsigned long	exper;		// 19
	unsigned char	training;	// 23
	unsigned short	primary;	// 24
	unsigned short	second;		// 26
	unsigned short	oppressr;	// 28
	unsigned short	IVr[2];		// 32
	unsigned short	SVr[2];		// 36
	unsigned short	status3;	// 40
	unsigned char	d_3[5];		// 41
	unsigned char	acty2;		// 46
	unsigned char	SN;			// 47
	unsigned short	V1;			// 48
	unsigned short	V2;			// 50
	unsigned char	d_4[29];	// 52
	unsigned char	food;		// 81
	unsigned char	d_5[7];		// 82
	unsigned char	name[16];	// 89
	// 105 bytes
} NBUF, *PNBUF;

// first place 105 * 356 + 4 in U7NBUF.DAT
typedef struct READIED
{
	union {
		unsigned short position[12];
		struct {
			unsigned short bag;			// 0, referent to IBUF
			unsigned short leftHand;	// 1
			unsigned short rightHand;	// 2
			unsigned short belt;		// 3
			unsigned short neck;		// 4
			unsigned short chest;		// 5, armor
			unsigned short i6;			// 6
			unsigned short i7;			// 7
			unsigned short bolts;		// 8
			unsigned short i9;			// 9
			unsigned short legs;		// 10
			unsigned short feet;		// 11
		};
	};
} READIED, *PREADIED;

typedef struct PARTY
{
	unsigned short	member[8]; // referent (of Nth companion of the party in U7IBUF.DAT)
	unsigned char	count;	// (number of companion in party)
	char data[1];			// must contain str/int etc.. for the persons
} PARTY, *PPARTY;

// MAPCOORDS.DAT
typedef struct MAPCOORDS
{
	unsigned short x; // (X coordinate 0..3071)
	unsigned short y; // (Y coordinate 0..3071)
	unsigned char d1;
	unsigned char d2;
	unsigned char d3;
	unsigned short d4;
} MAPCOORDS, *PMAPCOORDS;


#pragma pack()



//
// Basic Type Definitions
//

typedef struct U7VECTOR2
{
	union {
		struct { int x, y; };
		int xy[2];
	};

	bool operator == ( const U7VECTOR2& v ) const
	{
		return (x == v.x && y == v.y);
	}
} U7VECTOR2, *PU7VECTOR2;

typedef U7VECTOR2 U7POINT, *PU7POINT;
typedef U7VECTOR2 U7SIZE, *PU7SIZE;

typedef struct U7RECT
{
	int left, top, right, bottom;

	bool IsIntersected( const U7RECT& x ) const
	{
		return !( left >= x.right || x.left >= right ||
			top >= x.bottom || x.top >= bottom );
	}
	int Width() const  { return right - left; }
	int Height() const { return bottom - top; }
} U7RECT, *PU7RECT;

typedef struct U7VECTOR3
{
	union {
		struct { int x, y, z; };
		int xyz[3];
	};

	bool operator == ( const U7VECTOR3& v ) const
	{
		return (x == v.x && y == v.y && z == v.z);
	}
} U7VECTOR3, *PU7VECTOR3;


//
// Helper
//
inline
const TFA* GetShapeTFA( const char* tfaFile, int shapeIndex )
{
	return (const TFA*)tfaFile + shapeIndex;
}

inline
XFORMTBL GetXformTables( const char* xformTblFile )
{
	return (XFORMTBL)(xformTblFile + ((PFLEXFILEENTRY)((PFLEXFILEHEADER)xformTblFile+1))->offset );
}

